﻿// ## 2023/10/27 # 铃 # 据点周围防御

namespace 据点周围防御
{
    const int 最大出发部队 = 3;

    bool 调试模式 = true;
    class Main
    {

        Main()
        {
            pk::bind(111, pk::trigger111_t(onTurnStart));

        }

        array<bool> arr_base_defence(据点_末, false);
        pk::list<pk::building @> list_enemy_building;
        pk::list<pk::person @> person_list;


        //---------------------------------------------------------------------------------------

        void onTurnStart(pk::force @force)
        {
            int force_id = force.get_id();
            if (force.get_id() != pk::get_current_turn_force_id()) return;

            if (pk::is_normal_force(force_id))
            {
                func_base_defence(force);
                pk::list<pk::unit @> unit_list = pk::get_unit_list(force);
                for (int i = 0; i < unit_list.count; i++)
                {
                    pk::unit @unit = unit_list[i];
                    if (!pk::is_alive(unit))
                        continue;

                    //  跳过玩家部队及非普通类型部队
                    if (pk::is_player_controlled(unit))
                        continue;

                    if (unit.order != 部队任务_移动)
                        continue;

                    if (pk::is_valid_base_id(unit.target))
                        continue;

                    if (pk::is_valid_pos(unit.target_pos))
                        continue;

                    // if (pk::is_valid_building_id(unit.target) and (unit.target < 据点_末 or unit.target >-1) and pk::get_building(unit.target).get_force_id() == force_id)
                    //     repair_unit_order(unit);

                    // if (!pk::is_valid_building_id(unit.target) and !pk::is_valid_pos(unit.target_pos))
                    //     repair_unit_order(unit);

                }
            }
        }



        void repair_unit_order(pk::unit @unit)
        {
            pk::trace(pk::decode(pk::get_name(unit)) + "修复部队任务,原来目标坐标" + unit.target_pos.x + " " + unit.target_pos.y + " 原来的" + unit.target);
            pk::set_order(unit, 部队任务_撤退, pk::get_building(pk::get_service(unit)).pos);
            pk::run_order(unit);
            pk::set_action_done(unit, true);
        }


        void func_base_defence(pk::force @force)
        {
            int force_id = force.get_id();
            for (int i = 0; i < 据点_末; i++)
            {
                arr_base_defence[i] = false;

                pk::building @base = pk::get_building(i);


                if (pk::is_player_controlled(base))
                    continue;


                bool cmd = false;

                if (base.get_force_id() == force_id and enemy_building_around(base))
                {
                    int max_count = pk::max(1, pk::min(5, 最大出发部队));
                    for (int push_count = 0; push_count < max_count; push_count++)
                        cmd = (PushCoveringUnit(base) or cmd);
                }

                if (cmd)
                    arr_base_defence[i] = true;
            }
        }

        bool PushCoveringUnit(pk::building @building)
        {
            // 选择部队出征
            pk::point dst_pos = list_enemy_building[0].pos;

            // if (dst_pos == -1)
            //     return false;

            pk::trace(pk::decode(pk::get_name(building)) + "目标" +  list_enemy_building[0].pos.x);


            // 出击战斗部队
            int unit_id = push_unit(building, dst_pos, true);
            if (unit_id != -1)
                return true;

            return false;
        }

        bool enemy_building_around(pk::building @building)
        {
            list_enemy_building.clear();

            pk::list<pk::building @> building_list = pk::get_building_list();
            for (int i = 0; i < building_list.count; i++)
            {
                pk::building @building0 = building_list[i];
                int force0_id = pk::get_building_id(building0.pos);
                int facility_id = building0.facility;

                if (building.energy < 120)
                    continue;

                if (!pk::is_alive(building0))
                    continue;
                if (facility_id >= 设施_火种 or facility_id == 设施_土垒 or facility_id == 设施_石壁 or facility_id <= 设施_港口)
                    continue;
                if (building.get_id() != pk::get_building_id(building0.pos))
                    continue;
                if (building0.get_force_id() == building.get_force_id())
                    continue;
                int spec_id = ch::to_spec_id(building0.get_id());
                if (ch::is_valid_spec_id(spec_id))
                {
                    auto spec_t = ch::get_spec_p(spec_id);
                    if (pk::get_city_count(pk::get_force(building.get_force_id())) > 3 and building.troops < spec_t.troops) continue;
                    if (pk::get_city_count(pk::get_force(building.get_force_id())) <= 3 and building.troops < spec_t.troops * 2) continue;

                }


                list_enemy_building.add(building0);
            }

            int count = pk::get_idle_person_list(building).count;

            pk::trace(pk::decode(pk::get_name(building)) + "发现敌方建筑数量" + list_enemy_building.count);

            if (count > 0 and pk::get_troops(building) >= 5000 and list_enemy_building.count > 0)
                return true;

            return false;
        }

        int push_unit(pk::building @src_base, pk::point target_pos, bool do_cmd = true)
        {

            int src_troops = pk::get_troops(src_base);
            if (src_troops <= 5000)
                return -1; // 兵力不足

            pk::list<pk::person @> person_list = pk::get_idle_person_list(src_base);
            if (person_list.count == 0)
                return -1; // 缺乏武将

            // 武将排序，优先考虑特技，按统率+武力排序。
            person_list.sort(function(a, b) {
                int a_score = a.stat[武将能力_武力] * UNIT_ATTR::攻武占比 + a.stat[武将能力_统率] * UNIT_ATTR::攻统占比 * 2 + a.stat[武将能力_智力] * UNIT_ATTR::防智占比;
                int b_score = b.stat[武将能力_武力] * UNIT_ATTR::攻武占比 + b.stat[武将能力_统率] * UNIT_ATTR::攻统占比 * 2 + b.stat[武将能力_智力] * UNIT_ATTR::防智占比;
                // 考虑武将能力
                return (a_score > b_score);
            });


            int reinforce_troops = pk::min(20000, pk::get_command(person_list[0]), pk::max(1, pk::get_troops(src_base) - 5000));

                        // 选择最佳的武器
            int ground_weapon_id = 兵器_剑;
            int ground_weapon_score = 0;
            int water_weapon_id = 兵器_走舸;
            int unit_troops = reinforce_troops;

            //*******************  陆地兵器  ******************
            // 选择陆上武器
            ground_weapon_id = get_ground_weapon(src_base, person_list[0], 1).first;
            ground_weapon_score = get_ground_weapon(src_base, person_list[0], 1).second;

            pk::trace(pk::decode(pk::get_name(person_list[0])) + get_weapon_name(ground_weapon_id) + "ground_weapon_id" + ground_weapon_id + "ground_weapon_score:" + ground_weapon_score + ",unit_troops :" + unit_troops);

            if (ground_weapon_score == 0 or ground_weapon_id == 0)
                return -1; // 兵器不足

            if (ground_weapon_id <= 兵器_战马)
            {
                unit_troops = pk::min(unit_troops, pk::get_weapon_amount(src_base, ground_weapon_id));
                if (unit_troops < 3000) return -1;
            }

            //********************  水上兵器  ***********************
            water_weapon_id = 兵器_走舸;

        //**********************  新粮草计算  **********************
        // 使用新的unit_distance计算部队消耗
        // 最终带粮基准值为路上*2+15回合战斗
            int base_weight = 5;

            if (pk::get_food(src_base) > pk::get_troops(src_base) * 5 and pk::get_food(src_base) > 80000)
                base_weight += 1;

            float rate = (base_weight * 2 + 15);
            if ((ground_weapon_id > 兵器_战马 and ground_weapon_id < 兵器_走舸) or ground_weapon_id == 兵器_戟) // 跑得慢的多加一些
                rate *= 1.25f;

            rate = pk::min(rate, 35.0); // 无论如何都不会超过350天,防止带的太多影响城市粮草
            int unit_food = int(unit_troops * rate / 10);
            if (unit_food > int(pk::get_food(src_base) * 0.5))
                return -1;

            if (do_cmd)
            {
                // 创建出征指令信息
                pk::com_march_cmd_info cmd;
                @cmd.base = @src_base;
                cmd.type = 部队类型_战斗;
                cmd.member[0] = person_list[0].get_id();
                cmd.gold = 0;
                cmd.troops = pk::max(1, unit_troops);
                cmd.weapon_id[0] = ground_weapon_id;
                cmd.weapon_id[1] = water_weapon_id;
                cmd.weapon_amount[0] = (is_siege_weapon(ground_weapon_id)) ? 1 : pk::max(1, unit_troops);
                cmd.weapon_amount[1] = 0;
                cmd.food = pk::min(50000, unit_food);

                cmd.order = 部队任务_移动;
                cmd.target_pos = target_pos; // 设置目标

                //ai出征优化::main.march_modify(cmd, 0/*0防御，1攻击，2建设，3运输*/);

                if (cmd.weapon_id[0] == -1)
                    return -1;

                //pk::info(pk::format("据点周围防御:部队:{},兵力:{},兵装:{},兵装数量:{},目标坐标:{},{},任务:{},", pk::decode(pk::get_name(person_list[0])),cmd.troops, cmd.weapon_id[0],cmd.weapon_amount[0],cmd.target_pos.x,cmd.target_pos.y, cmd.order));

                int unit_id = pk::command(cmd);



                // 执行出征指令

                if (unit_id == -1)
                    return -1;


                pk::unit @unit_cmd = pk::get_unit(unit_id);
                if (pk::is_alive(unit_cmd))
                {
                    unit_cmd.action_done = true; // 设置部队行动完成

                    string src_name = pk::decode(pk::get_name(src_base));
                    string unit_name = pk::decode(pk::get_name(pk::get_person(unit_cmd.leader)));
                    string weapon_name = get_weapon_name(unit_cmd.weapon);
                   // pk::person @target_p = pk::get_person(target.leader);
                    //string target_name = (pk::is_alive(target_p)) ? pk::decode(pk::get_name(target_p)) : "";
                    string order_str = get_order_info(unit_cmd.order);
                    if (调试模式) pk::info(pk::format("据点防御:部队: {}({}), {} 从({},{})向({},{})攻击", unit_name, weapon_name, src_name, unit_cmd.pos.x, unit_cmd.pos.y, target_pos.x, target_pos.y));

                    return unit_id;
                }
            }

            return -1;
        }



        //******************************************* 攻城工具判断   ****************************************************************
        bool is_siege_weapon(int weapon_id)
        {
            if (兵器_冲车 <= weapon_id and weapon_id <= 兵器_木兽)
                return true;
            return false;
        }

        //******************************************* 武器选择函数   ****************************************************************
        pk::int_int get_ground_weapon(pk::building @base, pk::person @leader, int type)
        {
            array<int> heishu_score(兵器_末, 0);    // 适性评分，是否可以参考副将补全
            array<bool> can_march(兵器_末, false);  // 是否有足够兵器可出征
            array<int> tech_score(兵器_末, 0);      // 已研究科技评分
            array<int> ling_score(兵器_末, 0);      // 根据态势调整
            array<int> skill_score(兵器_末, 0); // 总特技评分
            array<int> all_score(兵器_末, 0);       // 兵器和适性的总评分

            pk::force @force0 = pk::get_force(leader.get_force_id());
            for (int i = 0; i < 兵器_走舸; i++)
            {
                if (leader.tekisei[pk::equipment_id_to_heishu(i)] < 适性_B)
                    can_march[i] = false;

                else if ((i == 兵器_冲车 or i == 兵器_木兽) and type == 1)// 冲车木兽不在防御时出动
                    can_march[i] = false;

                else if ((i == 兵器_冲车 or i == 兵器_木兽 or i == 兵器_井阑 or i == 兵器_投石) and leader.tekisei[pk::equipment_id_to_heishu(i)] < 适性_S)//野战部队除非有S兵器,否则不出兵器
                    can_march[i] = false;

                else if (ch::check_weapon_able(base, i))
                    can_march[i] = true;

                if (can_march[i])
                {
                    if (i > 兵器_战马 and type == 0)  ling_score[i] -= 1; //进攻支援的时候,由于这是野战计算,因此还是要降低兵器权重.
                    if (i > 兵器_战马 and type == 1)  ling_score[i] -= 3; //防御支援的时候,井阑移动速度过慢,很不适合作为防御支援,除非不得已或者只适合井阑.

                    if (i == 兵器_剑)  heishu_score[i] = 0;
                    else heishu_score[i] = leader.tekisei[pk::equipment_id_to_heishu(i)];
                    tech_score[i] = ch::get_tech_score(force0, i);

                    int skill_id = leader.skill;
                    if (skill_id < 211 and skill_id >-1)
                    {
                    //pk::trace(pk::decode(pk::get_name(leader)) + get_weapon_name(i) + "i:" +i + "skill_id :" +skill_id );
                        skill_score[i] = ai出征优化::skill_score[skill_id][i];
                    }
                    all_score[i] = heishu_score[i] * 3 + tech_score[i] + skill_score[i] + ling_score[i];

                    //pk::trace(pk::decode(pk::get_name(leader)) + get_weapon_name(i) + "科技分:" + tech_score[i] + "特技分:" + skill_score[i] + ",适性分:" + heishu_score[i] * 3 + ",态势分:" + ling_score[i] + ",总分:" + all_score[i]);

                }
            }

            return pk::int_int(get_arr_max(all_score));
        }

        pk::int_int get_arr_max(array<int> score)
        {
            int best_score = 0;
            int id = 0;
            for (int i = 0; i < int(score.length); i++)
            {
                if (score[i] > best_score)
                {
                    best_score = score[i];
                    id = i;
                }
            }

            return pk::int_int(id, best_score);
        }

        //---------------------------------------------------------------------------------------

    //=======================================================================================

        string get_weapon_name(int weapon_id)
        {
            string weapon_name;
            switch (weapon_id)
            {
                case 兵器_剑:
                    weapon_name = "剑兵";
                    break;
                case 兵器_枪:
                    weapon_name = "枪兵";
                    break;
                case 兵器_戟:
                    weapon_name = "戟兵";
                    break;
                case 兵器_弩:
                    weapon_name = "弩兵";
                    break;
                case 兵器_战马:
                    weapon_name = "骑兵";
                    break;
                case 兵器_冲车:
                    weapon_name = "冲车";
                    break;
                case 兵器_井阑:
                    weapon_name = "井阑";
                    break;
                case 兵器_投石:
                    weapon_name = "投石";
                    break;
                case 兵器_木兽:
                    weapon_name = "木兽";
                    break;
                case 兵器_走舸:
                    weapon_name = "水军";
                    break;
                case 兵器_楼船:
                    weapon_name = "水军";
                    break;
                case 兵器_斗舰:
                    weapon_name = "水军";
                    break;
                default:
                    weapon_name = "??";
                    break;
            }
            return weapon_name;
        }

        string get_order_info(int order)
        {
            string name;
            switch (order)
            {
                case 0:
                    name = "部队任务_待命";
                    break;
                case 1:
                    name = "部队任务_移动";
                    break;
                case 2:
                    name = "部队任务_设置";
                    break;
                case 3:
                    name = "部队任务_攻击";
                    break;
                case 4:
                    name = "部队任务_撤退";
                    break;
                case 5:
                    name = "部队任务_拦截";
                    break;
                case 6:
                    name = "部队任务_护卫";
                    break;
                case 7:
                    name = "部队任务_攻城";
                    break;
                case 8:
                    name = "部队任务_修复";
                    break;
                case 9:
                    name = "部队任务_征服";
                    break;
                case 10:
                    name = "部队任务_补给";
                    break;
                case 11:
                    name = "部队任务_歼灭";
                    break;
                case 12:
                    name = "部队任务_追随";
                    break;
                case 13:
                    name = "部队任务_末";
                    break;
                default:
                    name = "部队任务_没有";
                    break;
            }

            return name;
        }

        //---------------------------------------------------------------------------------------
    };

    Main main;
}